﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
#if !WINDOWS_PHONE
using System.Web;
#endif
using System.IO;
using System.Threading.Tasks;
using Rud.Holmgren.TabulexLib.Common;

namespace Rud.Holmgren.TabulexLib
{

    class TbxNameValuePair
    {
        public string Name { get; set; }
        public string Value { get; set; }
    }

    class TbxNameValuePairList : IEnumerable<TbxNameValuePair>
    {
        private List<TbxNameValuePair> nvps = new List<TbxNameValuePair>();

        private void Recalc()
        {
            var contentStr = String.Join("&", nvps.Select(nvp => String.Format("{0}={1}", nvp.Name, HttpUtility.UrlEncode(nvp.Value))));
            Content = Encoding.UTF8.GetBytes(contentStr);
            ContentLength = Content.Length;
        }

        public long ContentLength { get; set; }
        public byte[] Content { get; set; }

        public void Add(string name, string value)
        {
            nvps.Add(new TbxNameValuePair() { Name = name, Value = value });
            Recalc();
        }

        public IEnumerator<TbxNameValuePair> GetEnumerator()
        {
            return nvps.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return nvps.GetEnumerator();
        }
    }


    class TbxWebserviceClient
    {
        private static string baseUri = "https://mobilapps.sfoweb.dk/ForaelderBackend/";
        private CookieCollection cookies = new CookieCollection();
        private ITbxModelSerializer serializer = new TbxFastJsonModelSerializer();

        private Cookie ParseCookie(string item)
        {
            var split = item.Split(';').Select(c => c.Trim()).ToList();
            var nameval = split[0];
            var nix = nameval.IndexOf('=');

            var name = nameval.Substring(0, nix);
            var value = nameval.Substring(nix + 1);

            return new Cookie(name, value);
        }

        private void ParseCookies(string cookiestr)
        {
            if (cookiestr == null) { return; }

            var lst = cookiestr.Split(',').Select(c => c.Trim()).ToList();

            foreach (var item in new string[] { lst.Last() })
            {
                var cookie = ParseCookie(item);

                if (cookies[cookie.Name] == null)
                {
                    cookies.Add(cookie);
                }
                else
                {
                    cookies[cookie.Name].Value = cookie.Value;
                }
            }
        }

        private IPromise<string> TryGetResponse(HttpWebRequest req)
        {
            var retval = new Deferred<string>();

            req.TryGetResponse().Fail(retval.Reject).Done(rsp =>
            {
                try
                {
                    ParseCookies(rsp.Headers["Set-Cookie"]);

                    using (var rspStr = new StreamReader(rsp.GetResponseStream()))
                    {
                        retval.Resolve(rspStr.ReadToEnd());
                    }
                }
                catch (Exception e)
                {
                    retval.Reject(e);
                }
            });

            return retval;
        }

        public IPromise<string> TransceiveRaw(string paramString, TbxNameValuePairList postData = null)
        {
            var retval = new Deferred<string>();
            var req = HttpWebRequest.Create(baseUri + paramString) as HttpWebRequest;

            req.UserAgent = "Android App";
            req.Headers["X-Tabulex-API-Version"] = "2";
            req.CookieContainer = new CookieContainer();
            req.CookieContainer.Add(new Uri(baseUri), cookies);

            if (postData != null)
            {
                req.Method = "POST";
                req.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
                req.ContentLength = postData.ContentLength;

                req.TryGetRequestStream().Fail(retval.Reject).Done(reqStream =>
                {
                    var data = postData.Content;
                    reqStream.Write(data, 0, data.Length);
                    reqStream.Dispose();

                    TryGetResponse(req).Fail(retval.Reject).Done(retval.Resolve);
                });
            }
            else
            {
                req.Method = "GET";
                TryGetResponse(req).Fail(retval.Reject).Done(retval.Resolve);
            }

            return retval;
        }

        public IPromise<T> Transceive<T>(string paramString, TbxNameValuePairList postData = null) where T : class
        {
            var retval = new Deferred<T>();

            TransceiveRaw(paramString, postData).Fail(retval.Reject).Done(rawstr =>
            {
                try
                {
                    retval.Resolve(serializer.DeSerialize<T>(rawstr));
                }
                catch (Exception e)
                {
                    retval.Reject(e);
                }
            });

            return retval;
        }

#if false
        public async Task<string> TransceiveRawAsync(string paramString, TbxNameValuePairList postData = null)
        {
            var req = HttpWebRequest.Create(baseUri + paramString) as HttpWebRequest;

            req.UserAgent = "Android App";
            req.Headers["X-Tabulex-API-Version"] = "2";
            req.CookieContainer = new CookieContainer();
            req.CookieContainer.Add(new Uri(baseUri), cookies);

            if (postData != null)
            {
                req.Method = "POST";
                req.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
                req.ContentLength = postData.ContentLength;
                
                using (var reqStream = await req.GetRequestStreamAsync())
                {
                    var data = postData.Content;
                    reqStream.Write(data, 0, data.Length);
                }
            }
            else
            {
                req.Method = "GET";
            }

            using (var rsp = await req.GetResponseAsync())
            {
                ParseCookies(rsp.Headers["Set-Cookie"]);

                using (var rspStr = new StreamReader(rsp.GetResponseStream()))
                {
                    return rspStr.ReadToEnd();
                }
            }
        }

        public async Task<T> TransceiveAsync<T>(string paramString, TbxNameValuePairList postData = null) where T : class
        {
            var rawstr = await TransceiveRawAsync(paramString, postData);
            var obj = serializer.DeSerialize<T>(rawstr);

            return obj;
        }
        
#endif

    }

}
